#include "config.h"
#ifdef LINUX_DRIVER
#include <sys/stat.h>
#include <malloc.h>
#include <sys/ioctl.h>
#include <linux/kd.h>
#ifdef GPM_MOUSEDRIVER
#include <gpm.h>
#endif
#include <unistd.h>
#include <fcntl.h>
#include <setjmp.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include "aalib.h"
#include "aaint.h"
#define MAXVCS 10
static int cursorx, cursory;
static FILE *vc[MAXVCS];
static int nvcs;
static int sizes[2][MAXVCS];
static int cursor_visible = 1;
static void linux_gotoxy (aa_context * c, int x, int y);

static void
linux_cursor (aa_context * c, int mode)
{
  cursor_visible = mode;
  linux_gotoxy (c, cursorx, cursory);
  if (mode)
	{
	  printf ("[?25h");
	}
  else
	printf ("[?25l");
  fflush (stdout);
}
static int
linux_init (__AA_CONST struct aa_hardware_params *p, __AA_CONST void *none,
			struct aa_hardware_params *dest, void **params)
{
  static int registered;
  static struct aa_font font;
  __AA_CONST static struct aa_hardware_params def = { NULL,
	AA_DIM_MASK | AA_REVERSE_MASK | AA_NORMAL_MASK | AA_BOLD_MASK |
	  AA_EXTENDED
  };
  struct stat sbuf;
  int major, minor;
  char fname[20];
  char tmp[256], *env;
  int vt;
  int i, y;
  int fd;
  *dest = def;
  fflush (stdout);
  fd = dup (fileno (stderr));
  fstat (fd, &sbuf);
  major = sbuf.st_rdev >> 8;
  vt = minor = sbuf.st_rdev & 0xff;
  close (fd);
  if (major != 4 || minor >= 64)
	return (0);
  if ((env = getenv ("AAVCS")) != NULL)
	{
	  int p1 = 0, p2;
	  nvcs = 0;
	  while (env[p1])
		{
		  while (env[p1] && env[p1] == ' ')
			p1++;
		  if (!env[p1])
			break;
		  p2 = 0;
		  while (env[p1] && env[p1] != ' ')
			tmp[p2++] = env[p1++];
		  tmp[p2] = 0;
		  vc[nvcs] = fopen (tmp, "w+");
		  if (vc[nvcs] == NULL)
			return 0;
		  nvcs++;
		}
	}
  else
	{
	  sprintf (fname, "/dev/vcsa%i", vt);
	  vc[0] = fopen (fname, "w+");
	  nvcs = 1;
	}
  if (vc[0] == NULL)
	return 0;
  if (!registered)
	{
	  char *data;
	  fd = open ("/dev/console", 0);
	  if (fd >= 0)
		{
		  char buf[32 * 1024];
		  struct consolefontdesc desc;
		  desc.chardata = buf;
		  desc.charcount = 1024;
		  i = ioctl (fd, GIO_FONTX, &desc);
		  close (fd);
		  if (i)
			{                        /*probably mda/hercules */
			  dest->font = &aa_font14;
			  dest->supported &= ~AA_DIM_MASK;
			  goto skip;
			}
		  font.name = "Font used by your console";
		  font.shortname = "current";
		  font.height = desc.charheight;
		  data = malloc (desc.charheight * 256);
		  font.data = data;
		  if (font.data == NULL)
			goto skip;
		  y = 0;
		  for (i = 0; i < 8192; i++)
			{
			  if (i % 32 < font.height)
				{
				  data[y] = desc.chardata[i], y++;
				}
			}
		  aa_registerfont (&font);
		  dest->font = &font;
		}
	}
skip:;
#ifdef GPM_MOUSEDRIVER
  aa_recommendlowmouse ("gpm");
#endif
  aa_recommendlowkbd ("linux");
  aa_recommendlowkbd ("slang");
  aa_recommendlowkbd ("curses");
  return 1;
}
static void
linux_uninit (aa_context * c)
{
  int i;
  for (i = 0; i < nvcs; i++)
	{
	  fclose (vc[i]);
	}
}
static void
linux_getsize (aa_context * c, int *width, int *height)
{
  int i;
  struct { unsigned char lines, cols, x, y; }
	scrn = { 0, 0, 0, 0};
  *width = 0;
  *height = 65536;
  for (i = 0; i < nvcs; i++)
	{
	  (void) fseek (vc[i], 0, SEEK_SET);
	  (void) fread (&scrn, 4, 1, vc[i]);
	  sizes[0][i] = scrn.cols;
	  sizes[1][i] = scrn.lines;
	  *width = *width + scrn.cols;
	  if (*height > scrn.lines)
		*height = scrn.lines;
	}
#ifdef GPM_MOUSEDRIVER
  gpm_mx = *width - 1;
  gpm_my = *height - 1;
#endif
}

static void
linux_flush (aa_context * c)
{
  int i;
  int x, y, xstart = 0, xend, end = aa_scrwidth (c) * aa_scrheight (c);
  unsigned char data[] = { 0x07, 0x08, 0x0f, 0x0f, 0x70, 0x17 };
  for (i = 0; i < nvcs; i++)
	{
	  fseek (vc[i], 4, 0);
	  for (y = 0; y < aa_scrheight (c); y++)
		{
		  int start = y * aa_scrwidth (c);
		  for (x = xstart; x < xstart + sizes[0][i]; x++)
			{
			  putc (c->textbuffer[x + start], vc[i]);
			  if (c->attrbuffer[x + start] < 7)
				putc (data[c->attrbuffer[x + start]], vc[i]);
			  else
				putc (0x27, vc[i]);
			}
		}
	  xstart += sizes[0][i];
	  fflush (vc[i]);
	}
}
static void
linux_gotoxy (aa_context * c, int x, int y)
{
  int n = 0;
  int i;
  struct
  {
	unsigned char lines, cols, x, y;
  }
  scrn;
  cursorx = x;
  cursory = y;
  for (i = 0; i < nvcs; i++)
	{
	  (void) fseek (vc[i], 0, SEEK_SET);
	  if (x >= n && x < n + sizes[0][i] && cursor_visible)
		{
		  scrn.x = x - n;
		  scrn.y = y;
		  scrn.lines = sizes[0][i];
		  scrn.cols = sizes[1][i];
		}
	  else
		{
		  scrn.x = 0;
		  scrn.y = 0;
		  scrn.lines = sizes[0][i];
		  scrn.cols = sizes[1][i];
		}
	  (void) fwrite (&scrn, 4, 1, vc[i]);
	  n += sizes[0][i];
	}
  fflush (vc[i]);
}
__AA_CONST struct aa_driver linux_d = {
  "linux", "Linux pc console driver 1.0",
  linux_init,
  linux_uninit,
  linux_getsize,
  NULL,
  NULL,
  linux_gotoxy,
  linux_flush,
  linux_cursor
};
#endif
